package owg.util.opengl;

import com.jogamp.opengl.GL;
import com.jogamp.opengl.GL3;
import com.jogamp.opengl.GLContext;

import owg.util.data.KryoInstance.K;
import owg.util.euclidian.MatrixStack;

public class ShaderProgram	
{
	public final static String PROJECTION = "projection_matrix";
	public final static String MODELVIEW = "modelview_matrix";
	public final static String TEXTURE = "texture_matrix";
	
	public final static String GLOBAL_COLOR = "global_color";
	
	public final static String COLOR_COUNT = "color_count";
	public final static String NORMAL_COUNT = "normal_count";
	public final static String TEXCOORD_COUNT = "texcoord_count";
	
	public final static String POSITION = "vertex_position";
	public final static String NORMAL = "vertex_normal";
	public final static String TEXCOORD = "vertex_texcoord";
	public final static String COLOR = "vertex_color";
	
	public final static String SAMPLER_0 = "sampler0";
	public final static String SAMPLER_SIZE_0 = "samplerSize0";
	
	transient public final EGL3 egl;
	public final String vertexName;
	public final int vertexShader;
	public final String fragmentName;
	public final int fragmentShader;
	public final int program;
	public transient final boolean isClone;
	
	@K private ShaderProgram() 
	{
		egl = (EGL3) EGL.getEGL(GLContext.getCurrentGL());
		isClone = true;
		assert egl != null;
		vertexName=null;vertexShader=0;fragmentName=null;fragmentShader=0;program=0;
	}
	
	ShaderProgram(EGL3 egl, String vertexName, String fragmentName, int shaderProgram, int vertexShader, int fragmentShader)
	{
		assert egl != null;
		isClone = false;
		this.egl = egl;
		this.vertexName = vertexName;
		this.fragmentName = fragmentName;
		this.program = shaderProgram;
		this.vertexShader = vertexShader;
		this.fragmentShader = fragmentShader;
	}
	public void enableAllAttribs(int vertexStride, int nVertexComps, int vertexPointer, 
		int nNormalComps, int normalPointer, int[] nTexCoords, int[] texCoordPointers, int nColorComps, int colorPointer) 
	{
		enableUniforms();
		enableVertexAttrib(vertexStride, nVertexComps, vertexPointer);
		enableNormalAttrib(vertexStride, nNormalComps, normalPointer);
		enableColorAttrib(vertexStride, nColorComps, colorPointer);
		enableTexcoordAttrib(vertexStride, nTexCoords, texCoordPointers);
	}
	public void enableAllAttribs(int vertexStride, int nVertexComps, int vertexPointer, 
		int nNormalComps, int normalPointer, int nTexCoords, int texCoordPointer, int nColorComps, int colorPointer) 
	{
		enableAllAttribs(vertexStride, nVertexComps, vertexPointer, nNormalComps, normalPointer, 
			new int[]{nTexCoords}, new int[]{texCoordPointer}, nColorComps, colorPointer);
	}
	

	public void uniform1f(String name, float f) {
		GL3 gl3 = egl.getGL();
		int loc = gl3.glGetUniformLocation( program, name );
		if(loc == -1)
			System.err.println("Warning: no such uniform: "+name);
		else
			gl3.glUniform1f(loc, f);
		
	}
	
	public void enableUniforms() 
	{
		GL3 gl3 = egl.getGL();
		
		int globalColorLocation = gl3.glGetUniformLocation( program, GLOBAL_COLOR );
		gl3.glUniform4fv(globalColorLocation, 1, egl.getActiveColor(), 0);
		
		int projectionMatrixLocation = gl3.glGetUniformLocation( program, PROJECTION );
		int modelviewMatrixLocation = gl3.glGetUniformLocation( program, MODELVIEW );
		int textureMatrixLocation = gl3.glGetUniformLocation( program, TEXTURE );
		
		float[] pm = egl.projectionMatrix().getArray().clone();
		MatrixStack.transpose4x4f(pm);
		float[] mm = egl.modelviewMatrix().getArray().clone();
		MatrixStack.transpose4x4f(mm);
		float[] tm = egl.textureMatrix().getArray().clone();
		MatrixStack.transpose4x4f(tm);
		gl3.glUniformMatrix4fv(projectionMatrixLocation, 1, false, pm, 0);
		gl3.glUniformMatrix4fv(modelviewMatrixLocation, 1, false, mm, 0);
		gl3.glUniformMatrix4fv(textureMatrixLocation, 1, false, tm, 0);
		
		int samplerLoc = gl3.glGetUniformLocation(program, SAMPLER_0);
		gl3.glUniform1i(samplerLoc, 0);
		int samplerSizeLoc = gl3.glGetUniformLocation(program, SAMPLER_SIZE_0);
		if(samplerSizeLoc != 0)
			gl3.glUniform2f(samplerSizeLoc, egl.getCurrentTextureWidth(), egl.getCurrentTextureHeight());
	}
	public void enableVertexAttrib(int vertexStride, int nVertexComps, int vertexPointer) 
	{
		GL3 gl3 = egl.getGL();
		
		int vertexLoc = gl3.glGetAttribLocation( program, "vertex_position" );
		gl3.glEnableVertexAttribArray( vertexLoc );
		gl3.glVertexAttribPointer( vertexLoc, nVertexComps, GL.GL_FLOAT, false, vertexStride, vertexPointer );
	}
	public void enableNormalAttrib(int vertexStride, int nNormalComps, int normalPointer) 
	{
		GL3 gl3 = egl.getGL();
		
		int normalCountLoc = gl3.glGetUniformLocation( program, NORMAL_COUNT );
		int normalLoc = gl3.glGetAttribLocation( program, "vertex_normal" );
		if(normalLoc != -1) 
		{
			gl3.glEnableVertexAttribArray( normalLoc );
			gl3.glVertexAttribPointer( normalLoc, 3, GL.GL_FLOAT, false, vertexStride, normalPointer );
		}
		gl3.glUniform1i(normalCountLoc, nNormalComps);
	}
	public void enableTexcoordAttrib(int vertexStride, int[] nTexCoords, int[] texCoordPointer) 
	{
		GL3 gl3 = egl.getGL();
		
		for(int i = 0; i<nTexCoords.length; i++)
		{
			int texCoordCountLoc = gl3.glGetUniformLocation( program, TEXCOORD_COUNT+i );
			int texCoordLoc = gl3.glGetAttribLocation( program, "vertex_texcoord"+i );
			if(texCoordLoc != -1) 
			{
				gl3.glEnableVertexAttribArray( texCoordLoc );
				gl3.glVertexAttribPointer( texCoordLoc, Math.max(1,nTexCoords[i]), GL.GL_FLOAT, false, vertexStride, texCoordPointer[i] );
			}
			if(texCoordCountLoc != -1)
			{
				gl3.glUniform1i(texCoordCountLoc, nTexCoords[i]);
			}
		}
	}
	
	public void enableColorAttrib(int vertexStride, int nColorComps, int colorPointer) 
	{
		GL3 gl3 = egl.getGL();
		
		int colorCountLoc = gl3.glGetUniformLocation( program, COLOR_COUNT );
		int colorLoc = gl3.glGetAttribLocation( program, "vertex_color" );
		if(colorLoc != -1) 
		{
			gl3.glEnableVertexAttribArray( colorLoc );
			gl3.glVertexAttribPointer( colorLoc, Math.max(1,nColorComps), GL.GL_FLOAT, false, vertexStride, colorPointer );
		}
		gl3.glUniform1i(colorCountLoc, nColorComps);
	}
	
	public void disableAttribs()
	{
		egl.getGL().glDisableVertexAttribArray( 0 );
		egl.getGL().glDisableVertexAttribArray( 1 );
		egl.getGL().glDisableVertexAttribArray( 2 );
		egl.getGL().glDisableVertexAttribArray( 3 );
	}
	
	@Override
	protected void finalize() throws Throwable 
	{
		if(!isClone)
		    ShaderLib.remove(egl, this);
	}
	
	@Override
	public String toString() 
	{
		return vertexName+":"+fragmentName;
	}
}